package com.rc.saas.tenant.common.base;

import com.mongodb.BasicDBObject;
import com.mongodb.MongoException;
import com.mongodb.client.MongoDatabase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.DbCallback;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapreduce.GroupBy;
import org.springframework.data.mongodb.core.mapreduce.GroupByResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * 支持自动重连
 * https://www.baeldung.com/queries-in-spring-data-mongodb
 * Created by sven on 2019/6/24
 */

public abstract class BaseMongoDaoImpl<T extends BaseMongoEntity> implements BaseMongoDao<T> {
    protected abstract Class<T> getEntityClass();

    @Autowired
    protected MongoTemplate mongoTemplate;

    @Override
    public void insert(T entity, String collectionName) {
        mongoTemplate.execute(new DbCallback<Object>() {
            public Object doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.insert(entity, collectionName);
            }
        });
    }

    @Override
    public void save(T entity, String collectionName) {
        mongoTemplate.execute(new DbCallback<Object>() {
            public Object doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.save(entity, collectionName);
            }
        });
    }

    @Override
    public void update(Query query, Update update, String collectionName) {
        //updateMulti 如果根据查询条件找到对应的多条记录是，全部更新
        //updateFirst 更改符合条件的第一个
        //upsert 顾名思义 update+insert 如果根据条件没有对应的数据,则执行插入
        //findAndModify 查询然后更新

        //Map<String, Object> map = null;
        //try {
        //    map = parseEntity(entity);
        //} catch (Exception e) {
        //    e.printStackTrace();
        //}
        //String id = null;
        //Object value = null;
        //Update update = new Update();
        //if (map != null && map.size() > 0) {
        //    for (String key : map.keySet()) {
        //        if (key.startsWith("{")) {
        //            id = key.substring(key.indexOf("{") + 1, key.indexOf("}"));
        //            value = map.get(key);
        //        } else {
        //            update.set(key, map.get(key));
        //        }
        //    }
        //}

        //mongoTemplate.updateFirst(new Query().addCriteria(Criteria.where(id).is(value)), update, getEntityClass());
        mongoTemplate.execute(new DbCallback<Object>() {
            public Object doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.updateMulti(query, update, getEntityClass(), collectionName);
            }
        });
    }

    @Override
    public void delete(Query query, String collectionName) {
        mongoTemplate.execute(new DbCallback<Object>() {
            public Object doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.findAndRemove(query, getEntityClass(), collectionName);
            }
        });
    }

    @Override
    public T findById(String id, String collectionName) {
        return mongoTemplate.execute(new DbCallback<T>() {
            public T doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.findById(id, getEntityClass(), collectionName);
            }
        });
    }

    @Override
    public T findOne(Query query, String collectionName) {
        return mongoTemplate.execute(new DbCallback<T>() {
            public T doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.findOne(query, getEntityClass(), collectionName);
            }
        });
    }

    @Override
    public List<T> find(Query query, String collectionName) {
        return mongoTemplate.execute(new DbCallback<List<T>>() {
            public List<T> doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.find(query, getEntityClass(), collectionName);
            }
        });
    }

    @Override
    public List<T> find(Query query, Sort sort, String collectionName) {
        //query.with(new Sort(new Order(Direction.DESC,"id")));
        //query.with(new Sort(Sort.Direction.DESC,"TIME1"));
        query.with(sort);

        return mongoTemplate.execute(new DbCallback<List<T>>() {
            public List<T> doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.find(query, getEntityClass(), collectionName);
            }
        });

        //return mongoTemplate.find(query, getEntityClass(), collectionName);
    }

    @Override
    public PageImpl<T> findPage(Query query, Sort sort, int pageIndex, int pageSize, String collectionName) {
        final Pageable pageableRequest = PageRequest.of(pageIndex, pageSize);
        query.with(sort);
        query.with(pageableRequest);

        return mongoTemplate.execute(new DbCallback<PageImpl<T>>() {
            public PageImpl<T> doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                // 查询总数
                int count = (int) mongoTemplate.count(query, getEntityClass(), collectionName);
                //使用mongo原生的驱动
                //mongoTemplate.getDb().getCollection(getEntityClass().getAnnotation(Document.class).collection()).count(countDBObject, new DBCollectionCountOptions().hintString(index))

                List<T> list = new ArrayList<T>();
                if (count > 0) {
                    list = mongoTemplate.find(query, getEntityClass(), collectionName);
                }
                return (PageImpl<T>) PageableExecutionUtils.getPage(list, pageableRequest, () -> count);
            }
        });

        // 查询总数
        //int count = (int) mongoTemplate.count(query, getEntityClass(), collectionName);
        //
        //List<T> list = mongoTemplate.find(query, getEntityClass(), collectionName);
        ////return (PageImpl<T>) PageableExecutionUtils.getPage(list, pageableRequest, () -> 0);
        //return (PageImpl<T>) PageableExecutionUtils.getPage(list, pageableRequest, () -> count);
    }

    @Override
    public List<T> findAll(Sort sort, String collectionName) {
        return mongoTemplate.execute(new DbCallback<List<T>>() {
            public List<T> doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.findAll(getEntityClass(), collectionName);
            }
        });

        //return mongoTemplate.findAll(getEntityClass(), collectionName);
    }

    @Override
    public Long count(Query query, String collectionName) {
        return mongoTemplate.execute(new DbCallback<Long>() {
            public Long doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.count(query, getEntityClass(), collectionName);
            }
        });

        //Long count = mongoTemplate.count(query, getEntityClass(), collectionName);
        //return count.intValue();
    }

    @Override
    public GroupByResults<T> group(Criteria criteria, String collectionName, GroupBy groupBy) {
        return mongoTemplate.execute(new DbCallback<GroupByResults<T>>() {
            public GroupByResults<T> doInDB(MongoDatabase db) throws MongoException, DataAccessException {
                return mongoTemplate.group(criteria, collectionName, groupBy, getEntityClass());
            }
        });
    }

    /**
     * aggregate原生语句执行方法
     * @param collectionName 表名
     * @param pipeline       管道操作语句。【:后面的内容，默认string,数字或者布尔加标识符#NUM,#BOO】
     * @return {“result”:[结果], "ok":查询状态}
     */
    //public Object dbAggregate(String collectionName, String pipeline) {
    //    BasicDBObject bdr = new BasicDBObject();
    //    bdr.put("aggregate", collectionName);
    //    char[] c = pipeline.toCharArray();
    //    bdr.put("pipeline", appendWithChar(c));
    //    return mongoTemplate.getCollection("$cmd").findOne(bdr);
    //}

}
